package hydrap2p.search;

import hydrap2p.ServiceLocation;
import hydrap2p.helper.ServiceConnector;
import hydrap2p.logging.Level;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.apache.thrift.TException;

public class Searches {
	private Listeners listeners;
	private Map<Integer, Search> searches;
	private Map<String, SearcherIface> searchers;
	
	public Searches(Listeners listeners)
	{
		this.listeners=listeners;
		searches=new TreeMap<Integer, Search>();
		searchers=new TreeMap<String, SearcherIface>();
	}
	
	public int startSearch(SearchQuery query)
	{
		int key=-1;
		Search value;
		synchronized(searches)
		{
			for (int i=0; i<searches.size()+10; i++)
			{
				if (!searches.containsKey(i))
				{
					key=i;
					break;
				}
			}
			value=new Search(key, searchers, listeners, query);
			searches.put(key, value);
		}
		listeners.newSearch(key, query, new LinkedList<String>(searchers.keySet()));
		value.start();
		SearchLogging.log(Level.INFO, "Search - Starting Search", "Successfully started search "+query.getName()+" ("+key+")");

		return key;
	}
	
	public int startSearchSome(SearchQuery query, List<String> providers)
	{
		int key=-1;
		Search value;
		Map<String, SearcherIface> somesearchers=new TreeMap<String, SearcherIface>();
		synchronized(searches)
		{
			for (int i=0; i<searches.size()+10; i++)
			{
				if (!searches.containsKey(i))
				{
					key=i;
					break;
				}
			}
			
			for (String provider : providers)
			{
				SearcherIface searcher = searchers.get(provider);
				if (searcher!=null)
					somesearchers.put(provider,searcher);
			}
			value=new Search(key, somesearchers, listeners, query);
			value.start();
			searches.put(key, value);
		}
		listeners.newSearch(key, query, new LinkedList<String>(somesearchers.keySet()));
		SearchLogging.log(Level.INFO, "Search - Starting Search", "Successfully started search "+query.getName()+" ("+key+")");
		
		return key;
	}

	public boolean stopSearch(int searchid) {
		Search temp=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		if (temp!=null)
		{
			temp.stop();
			SearchLogging.log(Level.INFO, "Search - Stopping Search", "Successfully stopped search "+temp.getSearchQuery().getName()+" ("+searchid+")");
			return true;
		}
		else
		{
			SearchLogging.log(Level.ERROR, "Search - Stopping Search", "Failed to stop search id# "+searchid+", could not be found");
			return false;
		}
	}
	
	public boolean restartSearch(int searchid)
	{
		Search temp=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		if (temp!=null)
		{
			temp.start();
			SearchLogging.log(Level.INFO, "Search - Restarting Search", "Successfully restarted search "+temp.getSearchQuery().getName()+" ("+searchid+")");
			return true;
		}
		else
		{
			SearchLogging.log(Level.ERROR, "Search - Restarting Search", "Failed to restart search id# "+searchid+", could not be found");
			return false;
		}
	}

	public boolean deleteSearch(int searchid)
	{
		Search temp=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		if (temp!=null)
		{
			temp.stop();
			synchronized(searches)
			{
				searches.remove(searchid);
			}
			listeners.delSearch(searchid);
			SearchLogging.log(Level.INFO, "Search - Deleting Search", "Successfully deleted search "+temp.getSearchQuery().getName()+" ("+searchid+")");
			return true;
		}
		else
		{
			SearchLogging.log(Level.ERROR, "Search - Deleting Search", "Failed to delete search id# "+searchid+", could not be found");
			return false;
		}
	}
	
	public boolean addSearchResult(int searchid, SearchResult result)
	{
		Search temp=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		if (temp!=null)
		{
			int resultid=-1;
			try {
				resultid=temp.addSearchResult(result);
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
			if (resultid==-1)
				return false;
			else
				return true;
		}
		else
			return false;
	}
	
	public List<SearchResult> getSearchResults(int searchid)
	{
		Search temp=null;
		List<SearchResult> results=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		if (temp!=null)
			results=temp.getSearchResults();
		return results;
	}
	
	public boolean searchFinished(int searchid, String provider)
	{
		Search temp=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		if (temp!=null)
		{
			temp.searchFinished(provider);
			return true;
		}
		return false;
	}
	
	public Map<Integer, Search> getSearches()
	{
		return searches;
	}
	
	public Search getSearch(int searchid)
	{
		Search temp=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		return temp;	
	}
	
	public List<String> getSearchActiveProviders(int searchid)
	{
		Search temp=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		if (temp!=null)
		{
			return temp.getSearchActiveProviders();
		}
		return null;
	}
	public List<String> getSearchTotalProviders(int searchid)
	{
		Search temp=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		if (temp!=null)
		{
			return temp.getSearchTotalProviders();
		}
		return null;
	}
	
	public SearchQuery getSearchQuery(int searchid)
	{
		Search temp=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		if (temp!=null)
		{
			return temp.getSearchQuery();
		}
		return null;
	}
	
	public boolean registerSearchProvider(SearcherIface searcher)
	{
		synchronized(searchers)
		{
			searchers.remove(searcher.getName());
			searchers.put(searcher.getName(), searcher);	
		}
		SearchLogging.log(Level.INFO, "Search - Register Search Provider", "Search Provider "+searcher.getName()+" successfully registered");
		return true;
	}
	
	public List<String> listSearchProviders()
	{
		return new LinkedList<String>(searchers.keySet());
	}

	public boolean unregisterSearchProvider(SearcherIface searcher)
	{
		synchronized(searchers)
		{
			boolean ret=searchers.remove(searcher.getName())!=null;
			synchronized(searches)
			{
				for (int searchid:searches.keySet())
				{
					searchFinished(searchid,searcher.getName());
				}
			}
			SearchLogging.log(Level.INFO, "Search - Register Search Provider", "Search Provider "+searcher.getName()+" successfully unregistered");
			return ret;
		}
	}
	
	public boolean unregisterSearchProvider(ServiceLocation location)
	{
		boolean found=false;
		synchronized(searchers)
		{
			for (Entry<String, SearcherIface> searcher : searchers.entrySet())
			{
				if (searcher.getValue() instanceof SearcherThrift)
				{
					SearcherThrift thrifter=(SearcherThrift)searcher;
					if (thrifter.getLocation().equals(location))
					{
						searchers.remove(searcher.getKey());

						synchronized(searches)
						{
							for (int searchid:searches.keySet())
							{
								searchFinished(searchid,searcher.getKey());
							}
						}
						SearchLogging.log(Level.INFO, "Search - Register Search Provider", "Search Provider "+searcher.getKey()+" successfully unregistered");
						found=true;
						break;
					}
				}
			}
		}
		return found;
	}
	
	public long download(String downloadpath, String name, int searchid, int resultid) throws TException
	{
		Search temp=null;
		synchronized(searches)
		{
			temp=searches.get(searchid);
		}
		if (temp!=null)
		{
			SearchResult result=temp.getSearchResult(resultid);
			if (result!=null)
			{
				try {
					long uid=ServiceConnector.getDownloader().createDownload(result.sources, downloadpath, name, result.size);
					SearchLogging.log(Level.INFO, "Search - Downloading Result", "Started downloading result "+name);
					return uid;
				} catch (TException e) {
					throw new TException("Failed to contact Downloader");
				}
			}
			else
			{
				SearchLogging.log(Level.ERROR, "Search - Downloading Result", "Could not download search result, invalid resultid given");
				throw new TException("Could not download search result, invalid resultid given");
			}
		}
		else
		{
			SearchLogging.log(Level.ERROR, "Search - Downloading Result", "Could not download search result, invalid searchid given");
			throw new TException("Could not download search result, invalid searchid given");
		}
	}
}
